/*
 * Copyright (C) National Biorepository Project, KFSHRC, 2003
 * Created on Aug 26, 2003 by aaa
 *
 */
package sa.edu.kfshrc.nb.server.entities.beans;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Locale;
import java.util.Vector;

import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.FinderException;

import sa.edu.kfshrc.nb.server.Configurator;
import sa.edu.kfshrc.nb.server.database.PrimaryKey;
import sa.edu.kfshrc.nb.server.entities.support.Groups;
import sa.edu.kfshrc.nb.server.entities.support.PropertyConstraint;
import sa.edu.kfshrc.nb.server.entities.support.Validator;
import sa.edu.kfshrc.nb.server.exceptions.NBServerException;
import sa.edu.kfshrc.nb.server.logging.NBLevel;

/**
 * @author aaa
 * Created on Oct 14, 2003 at 03:11:34 PM.
 *
 * @ejb.bean name="PermissionRemoteHome"
 * 			 description="Permission entity bean"
 * 			 type="BMP"
 * 			 display-name="PermissionRemoteHome"
 * 			 jndi-name="PermissionRemoteHome"
 * 			 local-jndi-name="PermissionLocalHome"
 *
 * @ejb.pk class="sa.edu.kfshrc.nb.server.database.PrimaryKey"
 * 
 * @ejb.transaction type="Required"
 *  
 * @ejb.interface remote-pattern="PermissionRemote"
 * 				  local-pattern="PermissionLocal"
 * 				  extends="javax.ejb.EJBObject"
 * 				  local-extends="javax.ejb.EJBLocalObject"
 * 
 * @ejb.home remote-pattern="PermissionRemoteHome"
 * 			 local-pattern="PermissionLocalHome"
 * 			 extends="javax.ejb.EJBHome"
 * 			 local-extends="javax.ejb.EJBLocalHome"
 */
public class PermissionBean extends BaseEntityBean implements EntityBean
{
	// ----- Static members ---------------------------------------------------
	// ----- Permission statuses ----------------------------------------------
	// The precedence hierarchy is as follows: P_DENIED > P_GRANTED
	public static final short P_DENIED = 0;
	public static final short P_GRANTED = 1;

	// marker to represent 'wild-card' object table name
	public static final String MATCH_ALL_TABLE_NAMES = "*";
	
	// Store the table name in a class-wide variable
	private static final String tableName = "t_permissions";

	// ----- Instance members -------------------------------------------------
	private String actionName = null;
	private short actionVersion = 1;
	private PrimaryKey objectID = null;
	private String objectTableName = null;
	private String objectGroup = null;
	private PropertyConstraint objectConstraint = new PropertyConstraint();
	private PrimaryKey assignerID = null;
	private PrimaryKey assignerRankID = null;
	private PrimaryKey assigneeID = null;
	private PrimaryKey assigneeRoleID = null;
	private short status = PermissionBean.P_DENIED;
	private String arabicDescription = null;
	private String englishDescription = null;
	private PrimaryKey dependsUponID = null;
	private long priorityLevel = 0;

	/**
	 * 
	 */
	public PermissionBean()
	{
		super();
		setEntityTableName(tableName);
	}

	private long computePriorityLevel()
	{
		// The priority level is a convenient shorthand that indicates how strong
		// a particular permission is compared to other permissions. The priority
		// is computed as follows. We look at each of the following attributes
		// and assign it a number, then sum them up but in different positions
		// within a single number (i.e. the units, the tens, the hundreds, etc.).
		// The most important component of a permission is its assigner's rank.
		// The rest of the attributes follow, and their order is obviously significant.
		// There are a total of seven attributes that contribute to the priority
		// of a permission: 
		//
		// rank: short
		// action: specific, not (based on name and version)
		// assignee: specific, match all-uninit
		// assignee role: specific, match all-uninit
		// object: specific, match all-uninit
		// table: specific, not
		// group: specific, match all-uninit
		// status: DENIED, GRANTED

		long l = 0;
		try
		{
			l = RankBean.lookupRank(assignerRankID) * 10000000L;

			if ( !actionName.equals(ActionBean.MATCH_ALL_ACTION_NAMES) && actionVersion != ActionBean.MATCH_ALL_ACTION_VERSIONS )
				l = l + 4000000L;
			else if ( !actionName.equals(ActionBean.MATCH_ALL_ACTION_NAMES) && actionVersion == ActionBean.MATCH_ALL_ACTION_VERSIONS )
				l = l + 3000000L;
			else if ( actionName.equals(ActionBean.MATCH_ALL_ACTION_NAMES) && actionVersion != ActionBean.MATCH_ALL_ACTION_VERSIONS )
				l = l + 2000000L;

			if ( assigneeID.isMatchingOne() )
				l = l + 100000L;

			if ( assigneeRoleID.isMatchingOne() )
				l = l + 10000L;

			if ( objectID.isMatchingOne() )
				l = l + 1000L;

			if ( !objectTableName.equals(PermissionBean.MATCH_ALL_TABLE_NAMES) )
				l = l + 100L;

			GroupBean gb = new GroupBean();
			gb.setName(objectGroup);
			if (gb.isMatchingOne())
				l = l + 10L;

			if ( status == PermissionBean.P_DENIED )
				l = l + 1L;

			return l;
		}
		catch (Exception e)
		{
			return 0;
		}
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public String getActionName()
	{
		return actionName;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public short getActionVersion()
	{
		return actionVersion;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PrimaryKey getAssigneeID()
	{
		return assigneeID;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PrimaryKey getAssigneeRoleID()
	{
		return assigneeRoleID;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PrimaryKey getAssignerID()
	{
		return assignerID;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PrimaryKey getAssignerRankID()
	{
		return assignerRankID;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PrimaryKey getDependsUponID()
	{
		return dependsUponID;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public String getArabicDescription()
	{
		return arabicDescription;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public String getObjectGroup()
	{
		return objectGroup;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PrimaryKey getObjectID()
	{
		return objectID;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public String getObjectTableName()
	{
		return objectTableName;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public short getStatus()
	{
		return status;
	}

	/**
	 * @param string
	 * 
	 * @ejb.interface-method
	 */
	public void setActionName(String string)
	{
		actionName = string;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param s
	 * 
	 * @ejb.interface-method
	 */
	public void setActionVersion(short s)
	{
		actionVersion = s;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param key
	 * 
	 * @ejb.interface-method
	 */
	public void setAssigneeID(PrimaryKey key)
	{
		assigneeID = key;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param key
	 * 
	 * @ejb.interface-method
	 */
	public void setAssigneeRoleID(PrimaryKey key)
	{
		assigneeRoleID = key;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param key
	 * 
	 * @ejb.interface-method
	 */
	public void setAssignerID(PrimaryKey key)
	{
		assignerID = key;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param key
	 * 
	 * @ejb.interface-method
	 */
	public void setAssignerRankID(PrimaryKey key)
	{
		assignerRankID = key;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param key
	 * 
	 * @ejb.interface-method
	 */
	public void setDependsUponID(PrimaryKey key)
	{
		dependsUponID = key;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param string
	 * 
	 * @ejb.interface-method
	 */
	public void setArabicDescription(String string)
	{
		arabicDescription = string;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param string
	 * 
	 * @ejb.interface-method
	 */
	public void setObjectGroup(String string)
	{
		objectGroup = string;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param key
	 * 
	 * @ejb.interface-method
	 */
	public void setObjectID(PrimaryKey key)
	{
		objectID = key;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param string
	 * 
	 * @ejb.interface-method
	 */
	public void setObjectTableName(String string)
	{
		objectTableName = string;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param s
	 * 
	 * @ejb.interface-method
	 */
	public void setStatus(short s)
	{
		status = s;
		priorityLevel = computePriorityLevel();
	}
	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public String getEnglishDescription()
	{
		return englishDescription;
	}

	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public PropertyConstraint getObjectConstraint()
	{
		return objectConstraint;
	}

	/**
	 * @param string
	 * 
	 * @ejb.interface-method
	 */
	public void setEnglishDescription(String string)
	{
		englishDescription = string;
		priorityLevel = computePriorityLevel();
	}

	/**
	 * @param string
	 * 
	 * @ejb.interface-method
	 */
	public void setObjectConstraint(PropertyConstraint pc)
	{
		objectConstraint = pc;
		priorityLevel = computePriorityLevel();
	}
	/**
	 * @return
	 * 
	 * @ejb.interface-method
	 */
	public long getPriorityLevel()
	{
		return priorityLevel;
	}
	/**
	 * @param l
	 * 
	 * @ejb.interface-method
	 */
	public void setPriorityLevel(long l)
	{
		priorityLevel = l;
	}

	protected void updateExtraFields(ResultSet resultSet) throws SQLException
	{
		actionName = resultSet.getString("actionname");
		actionVersion = resultSet.getShort("actionversion");
		objectID = PrimaryKey.getFromResultSet(resultSet, "objectid");
		objectTableName = resultSet.getString("objecttablename");
		objectGroup = resultSet.getString("objectgroup");
		objectConstraint.setConstraint(resultSet.getString("objectconstraint"));
		assignerID = PrimaryKey.getFromResultSet(resultSet, "assignerid");
		assignerRankID = PrimaryKey.getFromResultSet(resultSet, "assignerrankid");
		assigneeID = PrimaryKey.getFromResultSet(resultSet, "assigneeid");
		assigneeRoleID = PrimaryKey.getFromResultSet(resultSet, "assigneeroleid");
		status = resultSet.getShort("status");
		arabicDescription = resultSet.getString("arabicdescription");
		englishDescription = resultSet.getString("englishdescription");
		dependsUponID = PrimaryKey.getFromResultSet(resultSet, "dependsuponid");
		priorityLevel = resultSet.getLong("prioritylevel");
	}

	protected void updateExtraFieldsMapping()
	{
		extraFieldsMapping.put("actionname", actionName);
		extraFieldsMapping.put("actionversion", new Short(actionVersion));
		extraFieldsMapping.put("objectid", objectID);
		extraFieldsMapping.put("objecttablename", objectTableName);
		extraFieldsMapping.put("objectgroup", objectGroup);
		extraFieldsMapping.put("objectconstraint", objectConstraint.toString());
		extraFieldsMapping.put("assignerid", assignerID);
		extraFieldsMapping.put("assignerrankid", assignerRankID);
		extraFieldsMapping.put("assigneeid", assigneeID);
		extraFieldsMapping.put("assigneeroleid", assigneeRoleID);
		extraFieldsMapping.put("status", new Short(status));
		extraFieldsMapping.put("arabicdescription", arabicDescription);
		extraFieldsMapping.put("englishdescription", englishDescription);
		extraFieldsMapping.put("dependsuponid", dependsUponID);
		priorityLevel = computePriorityLevel();
		extraFieldsMapping.put("prioritylevel", new Long(priorityLevel));
	}

	public Hashtable validate(Locale locale, boolean checkID)
	{
		String result = null;
		Hashtable results = new Hashtable();
		Validator validator = new Validator(locale);

		validateBaseFields(results, locale, checkID);

		if (Configurator.getBasicValidationEnabled())
		{
			result = validator.checkString(actionName, false, new Integer(1), new Integer(100), null, false);
			if (result != null)
				results.put("actionname", result);

			result = validator.checkShort(actionVersion, null, null, null);
			if (result != null)
				results.put("actionversion", result);

			result = validator.checkString(objectTableName, false, new Integer(1), new Integer(100), null, false);
			if (result != null)
				results.put("objecttablename", result);

			result = validator.checkPrimaryKey(objectID, false, false, null);
			if (result != null)
				results.put("objectid", result);

			result = validator.checkString(objectGroup, false, new Integer(1), new Integer(20), null, false);
			if (result != null)
				results.put("objectgroup", result);

			result = validator.checkString(objectConstraint.toString(), false, new Integer(0), new Integer(300), null, false);
			if (result != null)
				results.put("objectconstraint", result);

			result = validator.checkPrimaryKey(assignerID, false, true, null);
			if (result != null)
				results.put("assignerid", result);

			result = validator.checkPrimaryKey(assignerRankID, false, true, null);
			if (result != null)
				results.put("assignerrankid", result);

			result = validator.checkPrimaryKey(assigneeID, false, false, null);
			if (result != null)
				results.put("assigneeid", result);

			result = validator.checkPrimaryKey(assigneeRoleID, false, false, null);
			if (result != null)
				results.put("assigneeroleid", result);

			short statusChoices[] = {P_DENIED,P_GRANTED};
			result = validator.checkShort(status, null, null, statusChoices);
			if (result != null)
				results.put("status", result);

			result = validator.checkPrimaryKey(dependsUponID, false, false, null);
			if (result != null)
				results.put("dependsuponid", result);

			result = validator.checkString(englishDescription, false, new Integer(1), new Integer(200), null, false);
			if (result != null)
				results.put("englishdescription", result);

			result = validator.checkString(arabicDescription, false, new Integer(1), new Integer(200), null, false);
			if (result != null)
				results.put("arabicdescription", result);

			result = validator.checkLong(priorityLevel, null, null, null);
			if (result != null)
				results.put("prioritylevel", result);

		}

		if (results.isEmpty())
			return null;
		return results;
	}

	/**
	 * @ejb.interface-method
	 */
	public Hashtable getAllFields(short format, Locale locale) throws NBServerException
	{
		Hashtable fields = super.getAllFields(format, locale);

		// Override if necessary
		if ((format & FMT_LOOKUPIDS) > 0 && locale != null && locale.getLanguage().equalsIgnoreCase("ar"))
		{
			fields.put("assignerrankid", RankBean.lookup(assignerRankID).get("arabicdescription"));
			fields.put("assigneeroleid", RoleBean.lookup(assigneeRoleID).get("arabicdescription"));
		}
		else if ((format & FMT_LOOKUPIDS) > 0 && locale != null && locale.getLanguage().equalsIgnoreCase("en"))
		{
			fields.put("assignerrankid", RankBean.lookup(assignerRankID).get("englishdescription"));
			fields.put("assigneeroleid", RoleBean.lookup(assigneeRoleID).get("englishdescription"));
		}
		return fields;
	}

	// This function was automatically generated but may need to be modified
	// =-=> IT WAS MODIFIED: priorityLevel = computePriorityLevel()
	/**
	 * @ejb.interface-method
	 */
	public boolean setAllFields(Hashtable fields)
	{
		try
		{
			if (fields == null || fields.isEmpty())
				return true;

			if (!setBaseFields(fields)) return false;

			if (fields.containsKey("actionname"))
				actionName = (String) fields.get("actionname");
			if (fields.containsKey("actionversion"))
				actionVersion = ((Short) fields.get("actionversion")).shortValue();
			if (fields.containsKey("objecttablename"))
				objectTableName = (String) fields.get("objecttablename");
			if (fields.containsKey("objectid"))
				objectID = new PrimaryKey(fields.get("objectid").toString());
			if (fields.containsKey("objectgroup"))
				objectGroup = (String) fields.get("objectgroup");
			if (fields.containsKey("objectconstraint"))
				objectConstraint = new PropertyConstraint((String) fields.get("objectconstraint"));
			if (fields.containsKey("assignerid"))
				assignerID = new PrimaryKey(fields.get("assignerid").toString());
			if (fields.containsKey("assignerrankid"))
				assignerRankID = new PrimaryKey(fields.get("assignerrankid").toString());
			if (fields.containsKey("assigneeid"))
				assigneeID = new PrimaryKey(fields.get("assigneeid").toString());
			if (fields.containsKey("assigneeroleid"))
				assigneeRoleID = new PrimaryKey(fields.get("assigneeroleid").toString());
			if (fields.containsKey("status"))
				status = ((Short) fields.get("status")).shortValue();
			if (fields.containsKey("dependsuponid"))
				dependsUponID = new PrimaryKey(fields.get("dependsuponid").toString());
			if (fields.containsKey("englishdescription"))
				englishDescription = (String) fields.get("englishdescription");
			if (fields.containsKey("arabicdescription"))
				arabicDescription = (String) fields.get("arabicdescription");
			//if (fields.containsKey("prioritylevel"))
			//	priorityLevel = ((Long) fields.get("prioritylevel")).longValue();
			priorityLevel = computePriorityLevel();
			return true;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}

	/**
	* @ejb.create-method
	*/
	public PrimaryKey ejbCreate(Hashtable parameters) throws CreateException
	{
		try
		{
			if (!setAllFields(parameters))
				throw new CreateException("Unable to set fields during creation");

			return createBean(new Groups((String) parameters.get("groups")),
				((Short) parameters.get("version")).shortValue(),
				parameters);
		}
		catch (Exception e)
		{
			throw new CreateException(e.getMessage());
		}
	}

	public void ejbPostCreate(Hashtable parameters)
	{
		(Configurator.getMainLogger()).log(NBLevel.NB_TRACE, "Entered ejbPostCreate");
	}

	/**
	 * @ejb.create-method
	 */
	public PrimaryKey ejbCreate(Groups inGroups, short inVersion, String inActionName, short inActionVersion, PrimaryKey inObjectID,
		String inObjectTableName, String inObjectGroup, PropertyConstraint inObjectConstraint, PrimaryKey inAssignerID, PrimaryKey inAssignerRankID, 
		PrimaryKey inAssigneeID, PrimaryKey inAssigneeRoleID, short inStatus, String inArabicDescription, String inEnglishDescription, 
		PrimaryKey inDependsUponID, long inPriorityLevel) 
		throws CreateException
	{
		actionName = inActionName;
		actionVersion = inActionVersion;
		objectID = inObjectID;
		objectTableName = inObjectTableName;
		objectGroup = inObjectGroup;
		objectConstraint = inObjectConstraint;
		assignerID = inAssignerID;
		assignerRankID = inAssignerRankID;
		assigneeID = inAssigneeID;
		assigneeRoleID = inAssigneeRoleID;
		status = inStatus;
		arabicDescription = inArabicDescription;
		englishDescription = inEnglishDescription;
		dependsUponID = inDependsUponID;

		priorityLevel = computePriorityLevel(); // the input priority level should be ignored

		return createBean(inGroups, inVersion, null);
	}

	public void ejbPostCreate(Groups inGroups, short inVersion, String inActionName, short inActionVersion, PrimaryKey inObjectID,
		String inObjectTableName, String inObjectGroup, PropertyConstraint inObjectConstraint, PrimaryKey inAssignerID, PrimaryKey inAssignerRankID, 
		PrimaryKey inAssigneeID, PrimaryKey inAssigneeRoleID, short inStatus, String inArabicDescription, String inEnglishDescription, 
		PrimaryKey inDependsUponID, long inPriorityLevel)
	{
		(Configurator.getMainLogger()).log(NBLevel.NB_TRACE, "Entered ejbPostCreate");
	}

	// Note: it appears necessary to put the the wrappers for the following two finder
	// methods in the derived class like this one; if you try to stick them in the 
	// BaseEntityBean class, the code works fine, but then Xdoclet fails to recognize 
	// these finders...
	public PrimaryKey ejbFindByPrimaryKey(PrimaryKey inID) throws FinderException
	{
		return findByPrimaryKey(inID);
	}

	public Collection ejbFindAll() throws FinderException
	{
		return findAll();
	}

	public Collection ejbFindByCoreFields(PrimaryKey inAssigneeID, Collection inAssigneeRoles,
		String inActionName, short inActionVersion, String inObjectTableName, PrimaryKey inObjectID, 
		String inObjectGroup) 
		throws FinderException
	{
		Iterator iterator = inAssigneeRoles.iterator();
		if (!iterator.hasNext())
			throw new FinderException("Missing assignee roles to search by!");

		Vector parameters = new Vector();
		String condition = "where (assigneeID = ? OR assigneeID = ?) and " +
			"(actionName = ? OR actionName = ?) and " +
			"(actionVersion = ? OR actionVersion = ?) and " +
			"(objectTableName = ? OR objectTableName = ?) and " +
			"(objectID = ? OR objectID = ?) and " +
			"(objectGroup = ? OR objectGroup = ?) and " +
			"(assigneeRoleID IN (";

		parameters.add(inAssigneeID);		
		System.out.println("---> AssigneeID: " + inAssigneeID); // TODO: rm
		parameters.add(PrimaryKey.MATCHES_ALL);

		parameters.add(inActionName);
		System.out.println("---> Action Name: " + inActionName); // TODO: rm
		parameters.add(ActionBean.MATCH_ALL_ACTION_NAMES);
		parameters.add(new Short(inActionVersion));
		System.out.println("---> Action Version: " + inActionVersion); // TODO: rm
		parameters.add(new Short(ActionBean.MATCH_ALL_ACTION_VERSIONS));

		parameters.add(inObjectTableName);
		System.out.println("---> Object Table Name: " + inObjectTableName); // TODO: rm
		parameters.add(PermissionBean.MATCH_ALL_TABLE_NAMES);

		parameters.add(inObjectID);
		System.out.println("---> Object ID: " + inObjectID); // TODO: rm
		parameters.add(PrimaryKey.MATCHES_ALL);
		
		parameters.add(inObjectGroup);
		System.out.println("---> Object Group: " + inObjectGroup); // TODO: rm
		parameters.add(GroupBean.MATCH_ALL_GROUP_NAMES);

		do
		{
			condition = condition + " ? ";
			PrimaryKey roleID = (PrimaryKey) iterator.next(); 
			parameters.add(roleID);
			System.out.println("--->    Assignee Role: " + roleID.toString()); // TODO: rm
			if (iterator.hasNext())
				condition = condition + ",";
			else
				break;
		} while (true);

		condition = condition + ") OR assigneeRoleID = ?)";
		parameters.add(PrimaryKey.MATCHES_ALL);
		
		condition = condition + " ORDER BY priorityLevel DESC";

		return findByCondition(condition, parameters);
	}
}
